package org.xq.softcup.mq.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.xerial.snappy.Snappy;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 零拷贝文件读写工具类
 */
@Slf4j
@Component
public class FileUtil {

    RandomAccessFile randomAccessFile;
    String filePath; //文件路径
    //  操作系统提供的一个内存映射的机制的类
    FileChannel fileChannel;
    MappedByteBuffer writeMap;

    private Lock lock = new ReentrantLock();

    public RandomAccessFile getRandomAccessFile(String filePath) throws FileNotFoundException {
        return new RandomAccessFile(new File(filePath), "rw");
    }

    public synchronized RandomAccessFile getRandomAccessFile(String filePath, String mode) {
        if (randomAccessFile == null) {
            try {
                randomAccessFile = new RandomAccessFile(new File(filePath), "rw");
//                fileChannel = randomAccessFile.getChannel();
//                writeMap = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, (long) 1024 * 1024 * 1024);
            } catch (FileNotFoundException e) {
                log.error("文件无法找到！");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return randomAccessFile;
    }

    /**
     * mmap 映射写入方法
     * @param writeMap
     * @param data
     * @param offset
     * @return
     */
    public AtomicLong write(MappedByteBuffer writeMap, byte[] data, int offset) throws IOException {
        writeMap.position(offset); //写入起始位置
        writeMap.put(data); // data.getBytes("utf-8")
        return new AtomicLong(writeMap.position());
    }

    /**
     * 读取
     * @param readMap
     * @param offset
     * @param size
     * @return
     * @throws IOException
     */
    public String read(MappedByteBuffer readMap, long offset, long size) throws IOException {
        byte[] byteArr = new byte[(int) size];
        readMap.position((int) offset);
        readMap.get(byteArr);
        String s = Snappy.uncompressString(byteArr, Charset.forName("utf-8"));
        return s;
    }



    /**
     *  顺序写
     * @param filePath
     * @param content
     * @param offset
     * @return
     */
    public AtomicLong fileWrite(String filePath, String content, final int offset) {
        try {
            getRandomAccessFile(filePath);
//            lock.tryLock();
            writeMap.position(offset);
            //使用Snappy压缩
            writeMap.put(Snappy.compress(content.getBytes("utf-8")));
//            writeMap.put(content.getBytes("utf-8"));
            return new AtomicLong(writeMap.position());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
//            lock.unlock();
        }
        return new AtomicLong(0);
    }
    /**
     * 顺序读
     * @param filePath
     * @param offset 读取其实位置
     * @param size 读取长度
     * @return
     */
    public String fileRead(String filePath, final long offset, final long size) throws IOException {
        try {
            //  操作系统提供的一个内存映射的机制的类
            FileChannel fileChannel = getRandomAccessFile(filePath).getChannel();
            lock.lock();
            MappedByteBuffer readMap = fileChannel.map(FileChannel.MapMode.READ_WRITE, offset, size);
            byte[] byteArr = new byte[(int) size];
            readMap.get(byteArr);
//            readMap.get(byteArr, 0, (int) size);
            //Snappy解压缩
            String s = Snappy.uncompressString(byteArr, Charset.forName("utf-8"));
//            return new String(byteArr, Charset.forName("utf-8"));
            return s;
//        } catch (InterruptedException e) {
//            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return null;
    }


}
